/*
-----------------------------------------------------------------------------
Filename:    DreddGame.cpp
Modified By: Michael Sebright
-----------------------------------------------------------------------------

This source file is based off of the
   ___                 __    __ _ _    _ 
  /___\__ _ _ __ ___  / / /\ \ (_) | _(_)
 //  // _` | '__/ _ \ \ \/  \/ / | |/ / |
/ \_// (_| | | |  __/  \  /\  /| |   <| |
\___/ \__, |_|  \___|   \/  \/ |_|_|\_\_|
      |___/                              
      Tutorial Framework
      http://www.ogre3d.org/tikiwiki/
-----------------------------------------------------------------------------
*/

#include "DreddGame.h"


//-------------------------------------------------------------------------------------
DreddGame::DreddGame(void)
    : mRoot(0),
    mCamera(0),
    mSceneMgr(0),
    mWindow(0),
    mResourcesCfg(Ogre::StringUtil::BLANK),
    mPluginsCfg(Ogre::StringUtil::BLANK),
    mTrayMgr(0),
    mDetailsPanel(0),
	gameOver(0),
	mCharInfo(0),
    mCursorWasVisible(false),
    mShutDown(false),
    mInputManager(0),
    mMouse(0),
    mKeyboard(0)
{

	level = 0; // set initial level
	isGameOver = false; 
	level2Timer = 0;
	level2Counter = 0;
}

//-------------------------------------------------------------------------------------
DreddGame::~DreddGame(void) {
    if (mTrayMgr) delete mTrayMgr;

    //Remove ourself as a Window listener
    Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
    windowClosed(mWindow);
    delete mRoot;
}

//-------------------------------------------------------------------------------------
bool DreddGame::configure(void) {
    // Show the configuration dialog and initialise the system
    // You can skip this and use root.restoreConfig() to load configuration
    // settings if you were sure there are valid ones saved in ogre.cfg
    if(mRoot->showConfigDialog()) {
        // If returned true, user clicked OK so initialise
        // Here we choose to let the system create a default rendering window by passing 'true'
        mWindow = mRoot->initialise(true, "DreddWorld Render Window");

        return true;
    } else {
        return false;
    }
}
//-------------------------------------------------------------------------------------
void DreddGame::chooseSceneManager(void) {
    // Get the SceneManager, in this case a generic one
    mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
}
//-------------------------------------------------------------------------------------@TODO top down view
void DreddGame::createCamera(void) {
    // Create the camera
    mCamera = mSceneMgr->createCamera("PlayerCam");

    mCamera->setPosition(Ogre::Vector3(0,10,0));
 
    mCamera->lookAt(Ogre::Vector3(0,0,-300));
	
}
//-------------------------------------------------------------------------------------
void DreddGame::createFrameListener(void) {
    Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
    OIS::ParamList pl;
    size_t windowHnd = 0;
    std::ostringstream windowHndStr;

    mWindow->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

    mInputManager = OIS::InputManager::createInputSystem( pl );

    mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
    mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));

    mMouse->setEventCallback(this);
    mKeyboard->setEventCallback(this);

    //Set initial mouse clipping size
    windowResized(mWindow);

    //Register as a Window listener
    Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);

    mTrayMgr = new OgreBites::SdkTrayManager("InterfaceName", mWindow, mMouse, this);
	 
	// Charcter information overlay
	Ogre::StringVector charInfo; 
	charInfo.push_back("Lives x");
	charInfo.push_back("Level ");

	mCharInfo = mTrayMgr->createParamsPanel(OgreBites::TL_NONE, "CharacterInfo", 200, charInfo);
	mTrayMgr->moveWidgetToTray(mCharInfo, OgreBites::TL_BOTTOMLEFT, 0);

	// game over overlay
	gameOver = mTrayMgr->createTextBox(OgreBites::TL_NONE, "GameOver", "\n\nGame Over:\n Press r to restart or ESC to quit",mWindow->getWidth(),mWindow->getHeight());
	gameOver->hide();

    mRoot->addFrameListener(this);
}
//-------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------@TODO add map
void DreddGame::createViewports(void) {
    // Create one viewport, entire window
    mViewport = mWindow->addViewport(mCamera);
    mViewport->setBackgroundColour(Ogre::ColourValue(0.4f, 0.0f, 0.8f));

    // Alter the camera aspect ratio to match the viewport
    mCamera->setAspectRatio(
        Ogre::Real(mViewport->getActualWidth()) / Ogre::Real(mViewport->getActualHeight()));
}
//-------------------------------------------------------------------------------------
void DreddGame::setupResources(void) {
    // Load resource paths from config file
    Ogre::ConfigFile cf;
    cf.load(mResourcesCfg);

    // Go through all sections & settings in the file
    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

    Ogre::String secName, typeName, archName;
    while (seci.hasMoreElements()) {
        secName = seci.peekNextKey();
        Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
        Ogre::ConfigFile::SettingsMultiMap::iterator i;
        for (i = settings->begin(); i != settings->end(); ++i) {
            typeName = i->first;
            archName = i->second;
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
                archName, typeName, secName);
        }
    }
}
//-------------------------------------------------------------------------------------
void DreddGame::createResourceListener(void) {

}
//-------------------------------------------------------------------------------------
void DreddGame::loadResources(void) {
    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}
//-------------------------------------------------------------------------------------
void DreddGame::go(void) {
#ifdef _DEBUG
    mResourcesCfg = "resources_d.cfg";
    mPluginsCfg = "plugins_d.cfg";
#else
    mResourcesCfg = "resources.cfg";
    mPluginsCfg = "plugins.cfg";
#endif

    if (!setup())
        return;

    mRoot->startRendering();

    // clean up
    destroyScene(level);
}
//-------------------------------------------------------------------------------------
bool DreddGame::setup(void) {
    mRoot = new Ogre::Root(mPluginsCfg);

    setupResources();

    bool carryOn = configure();
    if (!carryOn) return false;

    chooseSceneManager();
    createCamera();
    createViewports();

    // Set default mipmap level (NB some APIs ignore this)
    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);

    // Create any resource listeners (for loading screens)
    createResourceListener();
    // Load resources
    loadResources();

	// create character
	mCharacter = new Dredd(mCamera);
	

    // Create the scene
    createScene(level);

    createFrameListener();

    return true;
};
//-------------------------------------------------------------------------------------
bool DreddGame::frameRenderingQueued(const Ogre::FrameEvent& evt) {
    if(mWindow->isClosed())
        return false;

    if(mShutDown)
        return false;


    //Need to capture/update each device
    mKeyboard->capture();
    mMouse->capture();

    mTrayMgr->frameRenderingQueued(evt);
	
	// send time since last fram to dredd character for processing 
	mCharacter->addTime(evt.timeSinceLastFrame);

	// Check if game is over
	if(isGameOver) {
		mCharInfo->hide();
		mTrayMgr->moveWidgetToTray(gameOver, OgreBites::TL_CENTER, 0);
		gameOver->show();
		return true;
	}
	

    if (!mTrayMgr->isDialogVisible()) {
		if(mCharInfo->isVisible()) { 
			mCharInfo->setParamValue(0, Ogre::StringConverter::toString(mCharacter->getLives()));
			mCharInfo->setParamValue(1, Ogre::StringConverter::toString(level));
		}
    }


	/* if all enemies are defeated and you reached final destination go to next level */
	if(enemyCount == 0) {
		if(mCharacter->getLocation().distance(destination) < DESTINATION_RADIUS) {
			destroyScene(level);
			level++;
			createScene(level);
			return true;
		}
	}

	/* instance for a timer level */
	if(level == 2) {
		level2Timer += evt.timeSinceLastFrame;

		/* send four enemies every 7 seconds */
		if(level2Timer >= 7 && level2Counter < 80) {
			level2Timer = 0;
			int i;
			SYSTEMTIME systemTime;
			GetSystemTime(&systemTime);
			srand(systemTime.wMilliseconds);

			/* randomly place enemy in one of four locations */
			for(i = 0; i < 4; i++) { 
				if(rand() % 4 == 0) {
					enemies.push_back(new WalkingEnemy(mCamera, Vector3::Vector3(-94, 0, -75)));
				} else if (rand() % 4 == 1) {
					enemies.push_back(new WalkingEnemy(mCamera, Vector3::Vector3(94, 0, -75)));
				} else if (rand() % 4 == 2) {
					enemies.push_back(new WalkingEnemy(mCamera, Vector3::Vector3(94, 0, -150)));
				} else if (rand() % 4 == 3) {
					enemies.push_back(new WalkingEnemy(mCamera, Vector3::Vector3(-94, 0, -150)));
				}
			}
			level2Counter += 4;
		}
	}

	/* interate through list of enemies */
	for(std::list<WalkingEnemy*>::iterator it = enemies.begin(); it != enemies.end(); it++) {

		/* updated enemies */
		(*it)->updateDest(mCharacter->getLocation());
		(*it)->animate(evt);
		if((*it)->isDead()) {
			continue;
		}

		Real distance;
		std::list<Bullet *> active = mCharacter->activeBullets(); // get list of active bullets from dredd
		std::list<Bullet *> activeTwo = (*it)->activeBullets(); // get list of active bulletse from enemies 
		if(!activeTwo.empty()) {

			/* check if an enemy bullet has hit dredd */
			for(std::list<Bullet *>::iterator it3 = activeTwo.begin(); it3 != activeTwo.end(); it3++) {
				distance = mCharacter->getLocation().distance((*it3)->getProjectileNode()->getPosition());
				if(distance < BULLET_WALKING_RADIUS_SUM) {

					/* have dredd loose a life, if he has 0 then game over */
					if(!mCharacter->die()) {
						isGameOver = true;
					}

					(*it3)->setInactive(); // allow bullet to be reloaded 
					break;
				}
			}
		}
		
		if(active.empty()) {
			continue;
		} else {

			/* check to see if Dredd's bullets has hit an enemy  */
			for(std::list<Bullet *>::iterator it2 = active.begin(); it2 != active.end(); it2++) {
				distance = (*it2)->getProjectileNode()->getPosition().distance((*it)->mNode->getPosition());
				if(distance < BULLET_WALKING_RADIUS_SUM) {
					(*it2)->setInactive(); // make bullet useable again
					(*it)->setAnimationDie(); // set enemy to die animation
					enemyCount--; // decrease enemyCount
					break;
				}
			}

		}
	}

    return true;
}
//-------------------------------------------------------------------------------------
bool DreddGame::keyPressed( const OIS::KeyEvent &arg ) {
    if (mTrayMgr->isDialogVisible()) return true;   // don't process any more keys if dialog is up

    if (arg.key == OIS::KC_ESCAPE) { // if escace quit
        mShutDown = true;
    } else if(arg.key == OIS::KC_R) { // if r reset
		destroyScene(level);
		level = 0;
		mCharacter->setLives(3);
		isGameOver = false;
		gameOver->hide();
		if(mCharInfo) {
			mCharInfo->show();
		}
		createScene(level);
	} else if(arg.key == OIS::KC_2) { // if 2 jump to level 2
		destroyScene(level);
		level = 2;
		createScene(level);

	}
 
	mCharacter->keyDown(arg); // send character keyDown event
    return true;
}

bool DreddGame::keyReleased( const OIS::KeyEvent &arg ) {
	mCharacter->keyUp(arg); // sencd character keyup event
    return true;
}

bool DreddGame::mouseMoved( const OIS::MouseEvent &arg ) {
    if (mTrayMgr->injectMouseMove(arg)) return true;
	mCharacter->mouseMoved(arg);// send charachter mouse moved event
    return true;
}

bool DreddGame::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) {
    if (mTrayMgr->injectMouseDown(arg, id)) return true;
	mCharacter->mouseDown(arg, id); // send character mouse down event
    return true;
}

bool DreddGame::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) {
    if (mTrayMgr->injectMouseUp(arg, id)) return true;
	mCharacter->mouseUp(arg, id); // send charachter mouse released event
    return true;
}

//Adjust mouse clipping area
void DreddGame::windowResized(Ogre::RenderWindow* rw) {
    unsigned int width, height, depth;
    int left, top;
    rw->getMetrics(width, height, depth, left, top);

    const OIS::MouseState &ms = mMouse->getMouseState();
    ms.width = width;
    ms.height = height;
}

//Unattach OIS before window shutdown (very important under Linux)
void DreddGame::windowClosed(Ogre::RenderWindow* rw) {
    //Only close for window that created OIS (the main window in these demos)
    if( rw == mWindow ) {
        if( mInputManager ) {
            mInputManager->destroyInputObject( mMouse );
            mInputManager->destroyInputObject( mKeyboard );

            OIS::InputManager::destroyInputSystem(mInputManager);
            mInputManager = 0;
        }
    }
}

